﻿// Orginal code from https://github.com/ademargomes/JsonRequest
// Copyright (c) 2012 Ademar Gomes
// Modified by Mikael Gonella-Bolduc

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using Newtonsoft.Json;

namespace WU.LoadTester.Lib.Service
{
        /// <summary>
        /// Create Http Request, using json, and read Http Response.
        /// </summary>
        public class JsonRequest
        {
            /// <summary>
            /// Url of http server wich request will be created to.
            /// </summary>
            public String URL { get; set; }

            /// <summary>
            /// HTTP Verb wich will be used. Eg. GET, POST, PUT, DELETE.
            /// </summary>
            public String Verb { get; set; }

            /// <summary>
            /// Request content, Json by default.
            /// </summary>
            public String Content
            {
                get { return "application/json"; }
            }

            /// <summary>
            /// Authentication settings
            /// </summary>
            public AuthSettings AuthSettings { get; set; }

            public HttpWebRequest HttpRequest { get; internal set; }
            public HttpWebResponse HttpResponse { get; internal set; }
            public CookieContainer CookieContainer = new CookieContainer();

            /// <summary>
            /// Constructor Overload that allows passing URL and the VERB to be used.
            /// </summary>
            /// <param name="url">URL which request will be created</param>
            /// <param name="verb">Http Verb that will be userd in this request</param>
            public JsonRequest(string url, string verb)
            {
                URL = url;
                Verb = verb;
            }

            /// <summary>
            /// Default constructor overload without any paramter
            /// </summary>
            public JsonRequest(string url)
            {
                URL = url;
                Verb = "GET";
            }

            public async Task<string> ExecuteAsync(string relativeUrl, object obj, string verb)
            {
                if (verb != null)
                    Verb = verb;

                string url = URL.TrimEnd(new char[] { '/' }) + "/" + relativeUrl.TrimStart(new char[] { '/' });
                HttpRequest = CreateRequest(url);

                WriteStream(obj);

                try
                {
                    HttpResponse = await (dynamic) HttpRequest.GetResponseAsync();
                }
                catch (WebException error)
                {
                    HttpResponse = (HttpWebResponse)error.Response;
                    return ReadResponseFromError(error);
                }

                return ReadResponse();
            }

            public async Task<string> ExecuteAsync(string relativeUrl, string verb)
            {
                if (URL == null)
                    throw new ArgumentException(Resources.Error_UrlIsNull);
                
                if (verb != null)
                    Verb = verb;
                
                string url = URL.TrimEnd(new char[] {'/'}) + "/" + relativeUrl.TrimStart(new char[] { '/'});
                HttpRequest = CreateRequest(url);

                try
                {
                    HttpResponse = await (dynamic)HttpRequest.GetResponseAsync();
                }
                catch (WebException error)
                {
                    HttpResponse = (HttpWebResponse)error.Response;
                    return ReadResponseFromError(error);
                }

                return ReadResponse(); 
            }

            public async Task<string> ExecuteAsync()
            {
                if (URL == null)
                    throw new ArgumentException(Resources.Error_UrlIsNull);

                HttpRequest = CreateRequest();

                try
                {
                    HttpResponse = await (dynamic) HttpRequest.GetResponseAsync();
                }
                catch (WebException error)
                {
                    HttpResponse = (HttpWebResponse)error.Response;
                    return ReadResponseFromError(error);
                }

                return ReadResponse();
            }

            internal HttpWebRequest CreateRequest()
            {
                var basicRequest = (HttpWebRequest)WebRequest.Create(URL);
                basicRequest.ContentType = Content;
                basicRequest.Headers.Add("x-li-format", "json");
                basicRequest.Method = Verb;
                basicRequest.CookieContainer = CookieContainer;

                AuthenticationHelper.SetCredentials(ref basicRequest, this.AuthSettings, URL);

                return basicRequest;
            }
            internal HttpWebRequest CreateRequest(string url)
            {
                var basicRequest = (HttpWebRequest)WebRequest.Create(url);
                basicRequest.ContentType = Content;
                basicRequest.Headers.Add("x-li-format", "json");
                basicRequest.Method = Verb;
                basicRequest.CookieContainer = CookieContainer;

                AuthenticationHelper.SetCredentials(ref basicRequest, this.AuthSettings, URL);

                return basicRequest;
            }


            internal void WriteStream(object obj)
            {
                if (obj != null)
                {
                    using (var streamWriter = new StreamWriter(HttpRequest.GetRequestStream()))
                    {
                        if (obj is string)
                            streamWriter.Write(obj);
                        else
                            streamWriter.Write(JsonConvert.SerializeObject(obj));
                    }
                }
            }

            internal String ReadResponse()
            {
                if (HttpResponse != null)
                    using (var streamReader = new StreamReader(HttpResponse.GetResponseStream()))
                        return streamReader.ReadToEnd();

                return string.Empty;
            }

            internal String ReadResponseFromError(WebException error)
            {
                using (var streamReader = new StreamReader(error.Response.GetResponseStream()))
                    return streamReader.ReadToEnd();
            }
        }
}
